#include "core/global-value.hpp"

/**
 * \file
 * \ingroup globalValue
 * cas::GlobalValue implementation.
 */

namespace cas
{
    SLOG_COMPONENT_DEFINE("GlobalValue");

    GlobalValue::GlobalValue(std::string name, std::string help, const std::string &initialValue)
        : m_name(name), m_help(help), m_initialValue(initialValue), m_currentValue(initialValue)
    {
        SLOG_FUNCTION(name << help << initialValue);
        GetVector()->push_back(this);
        InitializeFromEnv();
    }

    void
    GlobalValue::InitializeFromEnv(void)
    {
        SLOG_FUNCTION(this);
        const char *envVar = getenv("CAS_GLOBAL_VALUE");
        if (envVar == 0 || std::strlen(envVar) == 0)
        {
            return;
        }
        std::string env = envVar;
        std::string::size_type cur = 0;
        std::string::size_type next = 0;
        while (next != std::string::npos)
        {
            next = env.find(";", cur);
            std::string tmp = std::string(env, cur, next - cur);
            std::string::size_type equal = tmp.find("=");
            if (equal != std::string::npos)
            {
                std::string name = tmp.substr(0, equal);
                std::string value = tmp.substr(equal + 1, tmp.size() - equal - 1);
                if (name == m_name)
                {
                    m_initialValue = value;
                    m_currentValue = value;
                    return;
                }
            }
            cur = next + 1;
        }
    }

    std::string
    GlobalValue::GetName(void) const
    {
        SLOG_FUNCTION_NOARGS();
        return m_name;
    }

    std::string
    GlobalValue::GetHelp(void) const
    {
        SLOG_FUNCTION_NOARGS();
        return m_help;
    }

    std::string
    GlobalValue::GetValue() const
    {
        SLOG_FUNCTION_NOARGS();
        return m_currentValue;
    }

    bool
    GlobalValue::SetValue(const std::string &value)
    {
        SLOG_FUNCTION(&value);
        m_currentValue = value;
        return true;
    }

    void
    GlobalValue::Bind(std::string name, const std::string &value)
    {
        SLOG_FUNCTION(name << &value);
        for (Iterator i = Begin(); i != End(); i++)
        {
            if ((*i)->GetName() == name)
            {
                if (!(*i)->SetValue(value))
                {
                    SFATAL_ERROR("Invalid new value for global value: " << name);
                }
                return;
            }
        }
        SFATAL_ERROR("Non-existant global value: " << name);
    }

    bool
    GlobalValue::BindFailSafe(std::string name, const std::string &value)
    {
        SLOG_FUNCTION(name << &value);
        for (Iterator i = Begin(); i != End(); i++)
        {
            if ((*i)->GetName() == name)
            {
                return (*i)->SetValue(value);
            }
        }
        return false;
    }

    GlobalValue::Iterator
    GlobalValue::Begin()
    {
        SLOG_FUNCTION_NOARGS();
        return GetVector()->begin();
    }

    GlobalValue::Iterator
    GlobalValue::End()
    {
        SLOG_FUNCTION_NOARGS();
        return GetVector()->end();
    }

    void
    GlobalValue::ResetValue()
    {
        SLOG_FUNCTION(this);
        m_currentValue = m_initialValue;
    }

    bool
    GlobalValue::GetValueByNameFailSafe(std::string name, std::string &value)
    {
        SLOG_FUNCTION(name << &value);
        for (GlobalValue::Iterator gvit = GlobalValue::Begin(); gvit != GlobalValue::End(); ++gvit)
        {
            if ((*gvit)->GetName() == name)
            {
                value = (*gvit)->GetValue();
                return true;
            }
        }
        return false; // not found
    }

    void
    GlobalValue::GetValueByName(std::string name, std::string &value)
    {
        SLOG_FUNCTION(name << &value);
        if (!GetValueByNameFailSafe(name, value))
        {
            SFATAL_ERROR("Could not find GlobalValue named \"" << name << "\"");
        }
    }

    GlobalValue::Vector *
    GlobalValue::GetVector(void)
    {
        SLOG_FUNCTION_NOARGS();
        static Vector vector;
        return &vector;
    }

} // namespace ns3
